package com.demo.test.leak

import android.app.Activity
import android.os.Bundle
import android.os.SystemClock
import android.util.Log
import android.widget.TextView
import com.demo.Tags
import com.fortest.R

/**
泄露方式：非静态内部类默认持有外部类的引用——Thread
泄露原因：Activity开启了一个新线程执行任务，
这个任务是通过Thread(Runnable target)创建的，其中传入的target是个“匿名内部类”，是Runnable的子类，是非静态内部类（包括匿名内部类），默认持有外部类（Activity）的引用，
新线程的生命周期比Activity长，
当Activity退出时，任务还没执行完，Runnable对象持有Activity，导致Activity泄露。

泄露报告　－－　StrictMode：
2020-05-24 21:18:55.580 13280-13280/? E/StrictMode: class com.demo.test.leak.ActivityLeakH_Thread2; instances=2; limit=1
android.os.StrictMode$InstanceCountViolation: class com.demo.test.leak.ActivityLeakH_Thread2; instances=2; limit=1
at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)

泄露报告　－－　LeakCanary：
65751 bytes retained by leaking objects
Displaying only 1 leak trace out of 2 with the same signature
Signature: a3729bd8f9d775fdfbe12c821be46ca81ab5c6
┬───
│ GC Root: Local variable in native code
│
├─ java.lang.Thread instance
│    Leaking: UNKNOWN
│    Thread name: 'Leaking local variables'
│    ↓ Thread.target
│             ~~~~~~
├─ com.demo.test.leak.ActivityLeakH_Thread2$onCreate$thread$1 instance
│    Leaking: UNKNOWN
│    Anonymous class implementing java.lang.Runnable
│    ↓ ActivityLeakH_Thread2$onCreate$thread$1.this$0
│                                              ~~~~~~
╰→ com.demo.test.leak.ActivityLeakH_Thread2 instance
​     Leaking: YES (ObjectWatcher was watching this because com.demo.test.leak.ActivityLeakH_Thread2 received Activity#onDestroy() callback and Activity#mDestroyed is true)
​     key = d3f0c05a-1b89-4962-9034-37c4d376af01
​     watchDurationMillis = 13713
​     retainedDurationMillis = 8713
 **/
class ActivityLeakH_Thread2 : Activity() {
    val TAG = Tags.leakThread2
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_leak_common)
        val textView = findViewById<TextView>(R.id.helper_text)
        textView.text = "泄露方式：非静态内部类默认持有外部类的引用——Thread\n" +
                "泄露原因：Activity开启了一个新线程执行任务，\n" +
                "这个任务是通过Thread(Runnable target)创建的，其中传入的target是个“匿名内部类”，是Runnable的子类，是非静态内部类（包括匿名内部类），默认持有外部类（Activity）的引用，\n" +
                "新线程的生命周期比Activity长，\n" +
                "当Activity退出时，任务还没执行完，Runnable对象持有Activity，导致Activity泄露。"
        Log.d(TAG, "线程的本地变量，导致内存泄露")

        val thread = Thread {
            while (true) {
                Log.d(TAG, "running...")
                SystemClock.sleep(1000)
            }
        }
        thread.name = "Leaking local variables"
        thread.start()
    }


}
